/*
 * Copyright (C) 2008 Peter Kling
 *
 * This file is part of 'A Kind of Billiard', a graphical billiard simulation.
 *
 * 'A Kind of Billiard' is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any
 * later version.
 *
 * 'A Kind of Billiard' is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * 'A Kind of Billiard'. If not, see <http://www.gnu.org/licenses/>.
 */

package cg2.physics;

import cg2.mathematics.MathMatrix;
import cg2.mathematics.MathVector;
import cg2.mathematics.Mathematics;

/**
 * @brief A physical object modeling a ball.
 * 
 * In contrast to standard physical object, balls keep track of their orientation. Therefore they store rotational
 * information as angles around the three axis.
 * Note: currently there it is always assumed that balls role in the (x,z)-plane, so the angle for the y-axis is always
 *       zero.
 */
public class PhysicalBall extends PhysicalObject {
	protected double radius;
	protected MathMatrix orientation = new MathMatrix(4, 4);
	
	/**
	 * @brief Constructs a billiard ball.
	 */
	public PhysicalBall(double mass, double radius) {
		super(mass);
		this.radius = radius;
		orientation = Mathematics.getUnitMatrix(4);
	}
	
	/**
	 * @brief Returns a 4x4 matrix that represents the orientation of this object.
	 * 
	 * The matrix is a simple rotational matrix. We use a 4x4 matrix for easy interaction with the OpenGL state
	 * matrices.
	 */
	public MathMatrix getOrientation() {
		return orientation.clone();
	}
	
	/**
	 * @brief Returns the radius.
	 */
	public double getRadius() {
		return radius;
	}
	
	/**
	 * @brief In addition to the motion aspects of normal physical aspects, balls keep track of their orientation.
	 * 
	 * Note: Currently they do so only for movements in the (x,z)-plane.
	 */
	public void update() {
		MathVector oldPosition = position.clone();
		super.update();
		MathVector positionDifference = Mathematics.sub(position, oldPosition);
		
		// update orientation only for movements on the ground obstacle (currently this is done for all (x,z)-planes;
		// we check for movements in such planes with a tolerance of about 11.5 degree; the y-parts of movements with
		// smaller angles are neglected for orientation)
		// TODO: this is not very elegant/robust --> improve
		MathVector normal = Mathematics.getUnitVector(3, 2);
		if (Math.abs(Mathematics.mul(normal, positionDifference)/positionDifference.norm()) > 2.E-1)
			return;
		positionDifference.set(1, 0.);
		
		// check if we moved (if not, we do not need to update the orientation)
		if (positionDifference.norm() == 0.)
			return;
		
		// update orientation of the ball
		double alpha = positionDifference.norm()/radius;

		// cross product with (0, -1, 0) to get axis for rotation
		MathVector axis = new MathVector(3);
		axis.set(0,  positionDifference.get(2));
		axis.set(1,  0.                       );
		axis.set(2, -positionDifference.get(0));
		
		// update orientation matrix
		orientation = Mathematics.mul(Mathematics.get4DRotationMatrix(alpha, axis), orientation);
	}
}
